home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / SocketClasses / SktSocket.m < prev    next >
Encoding:
Text File  |  1992-07-23  |  33.9 KB  |  817 lines

  1. /***************************************************************************
  2. *                                                                          *
  3. * SktSocket.m                                                              *
  4. * Copyright 1992 by Nik A Gervae                                           *
  5. *                                                                          *
  6. * One of a set of three Objective-C classes (SktSocketManager, SktSocket,  *
  7. * and SktSocketUser) which implement a convenient interface to Berkeley    *
  8. * stream sockets under NeXTSTEP(r).  See the accompanying class            *
  9. * specifications (files with a .rtf or .spec suffix) for further           *
  10. * information.                                                             *
  11. *                                                                          *
  12. * NeXTSTEP is a registered trademark of NeXT Computer, Inc.                *
  13. *                                                                          *
  14. ****************************************************************************
  15. *                                                                          *
  16. * LICENSE                                                                  *
  17. *                                                                          *
  18. * This program is free software; you can redistribute it and/or modify     *
  19. * it under the terms of the GNU General Public License as published by     *
  20. * the Free Software Foundation.                                            *
  21. *                                                                          *
  22. * The program and this makefile are distributed in the hope that it will   *
  23. * be useful, but are provided "AS IS" AND WITHOUT ANY WARRANTY; without    *
  24. * any express or implied warranty of MERCHANTABILITY or FITNESS FOR A      *
  25. * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
  26. * Any use or distribution of the program and documentation must include    *
  27. * appropriate copyrights to acknowledge Nik A. Gervae and the Free         *
  28. * Software Foundation, Inc.                                                *
  29. *                                                                          *
  30. * You should have received a copy of the GNU General Public License        *
  31. * along with this program; if not, write to the Free Software              *
  32. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                *
  33. *                                                                          *
  34. ****************************************************************************
  35. *                                                                          *
  36. * VERSION HISTORY                                                          *
  37. *                                                                          *
  38. * Version numbers are simply dates in the form YYYYMMDD.  These represent  *
  39. * the date that version was finished.  Only significantly changed versions *
  40. * are reported here, or those versions requiring explanation of changes.   *
  41. * There may be many interim stages between dated versions.                 *
  42. *                                                                          *
  43. * DateVersion Primary Author  Notes                                        *
  44. * ----------- --------------- -------------------------------------------- *
  45. * 19920327    Nik A Gervae    First released version                       *
  46. * 19920723    Nik A Gervae    Actually released                            *
  47. *                                                                          *
  48. ***************************************************************************/
  49.  
  50. #import <errno.h>
  51. #import <limits.h>
  52. #import <stdio.h>
  53. #import <string.h>
  54.  
  55. #import <sys/types.h>
  56. #import <sys/socket.h>
  57. #import <sys/time.h>
  58. #import <netinet/in.h>
  59. #import <netdb.h>
  60. #import <fcntl.h>
  61.  
  62. #import "SktSocket.h"
  63.  
  64.  
  65. typedef struct {
  66.   @defs(SktSocketUser)
  67. } sktsocketuser;
  68.  
  69. /*
  70.  * See +initialize below for info on these.
  71.  */
  72. typedef id (*qOutputFunc)(id self, SEL _cmd, const char *output,
  73.                                  long int length, ...);
  74. static qOutputFunc   outputToSocket;
  75.  
  76. /***************************************************************************
  77. *                                                                          *
  78. * SktReportSocketMemError()                                                *
  79. *                                                                          *
  80. * Report appropriately based on whether the process is a server (has an    *
  81. * SktSocketManager), or a client.                                          *
  82. *                                                                          *
  83. ***************************************************************************/
  84. void SktReportSocketMemError(id self, char *message) {
  85.  
  86.   id manager;
  87.  
  88.   manager = [self manager];
  89.  
  90.   if (manager) {
  91.     [manager log:message, [self name], [self socketFd]];
  92.   }
  93.   else if (stderr) {
  94.     fprintf(stderr, message, [self name], [self socketFd]);
  95.   }
  96.   return;
  97. }
  98.  
  99. /***************************************************************************
  100. *                                                                          *
  101. * These are the constant strings used.  Feel free to translate them into   *
  102. * your favorite language.  Do be sure to keep all the % directives in      *
  103. * place, or change the code that accesses these strings.                   *
  104. *                                                                          *
  105. ***************************************************************************/
  106. #define STR_ErrorNoFd       "ERROR (%s): "
  107. #define STR_ErrorWithFd     "ERROR (%s %d): "
  108.  
  109. #define STR_CantAccept      STR_ErrorNoFd "accept() failed.\n"
  110. #define STR_CantSetFNDELAY  STR_ErrorWithFd "fcntl() can\'t set no delay.\n"
  111. #define STR_CantSetOpts     STR_ErrorWithFd "unable to set sender\'s " \
  112.                              "options.\n"
  113. #define STR_NewConnect      "(%s) New connection from %s [%s] " \
  114.                              "(socketFd %d)\n"
  115. #define STR_UnknownParens   "(unknown)"
  116. #define STR_Unknown         "unknown"
  117. #define STR_CantOpenSocket  STR_ErrorNoFd "could not open socket\n"
  118. #define STR_UnknownHost     STR_ErrorNoFd "%s -- unknown host %s\n"
  119. #define STR_CantConnectToServer STR_ErrorWithFd \
  120.                                  "could not connect to server\n"
  121. #define STR_ReadError       STR_ErrorWithFd "read()\n"
  122. #define STR_NoMoreInput     "(%s %d) readInput -- no more input\n"
  123. #define STR_WriteError      STR_ErrorWithFd "write()\n"
  124.  
  125. #define STR_ReallocFatalError  STR_ErrorWithFd "zone realloc failed.\n"
  126. #define STR_MallocFatalError   STR_ErrorWithFd "zone malloc failed.\n"
  127.  
  128.  
  129.  
  130. @implementation SktSocket
  131.  
  132. /***************************************************************************
  133. *                                                                          *
  134. * +initialize                                                              *
  135. *                                                                          *
  136. * This method caches the function pointers for the queueOutput:ofLength:   *
  137. * method.  Since these methods are covered by other methods, we want to    *
  138. * avoid a message send each time a cover is used.  This way, we only incur *
  139. * the cost of a function call. Hopefully that's faster. :-)                *
  140. *                                                                          *
  141. ***************************************************************************/
  142. + initialize
  143. {
  144.   static BOOL initted = NO;
  145.  
  146.  /*
  147.   * Only do this if we're initializing the class itself.  Having a
  148.   * subclass do it only resets the static variables, which is a waste.
  149.   */
  150.   if (NO == initted) {
  151.     outputToSocket = (qOutputFunc)
  152.       [[SktSocket class] instanceMethodFor:@selector(queueOutput:ofLength:)];
  153.     initted = YES;
  154.   }
  155.   return self;
  156. }
  157.  
  158. /***************************************************************************
  159. *                                                                          *
  160. * -init                                                                    *
  161. *                                                                          *
  162. ***************************************************************************/
  163. - init
  164. {
  165.   return [self notImplemented:_cmd];
  166. }
  167.  
  168. /***************************************************************************
  169. *                                                                          *
  170. * -_init                                                                   *
  171. *                                                                          *
  172. * Don't ever send this yourself.  It simply provides common functionality  *
  173. * for the other three init... methods.                                     *
  174. *                                                                          *
  175. * ERROR CONDITION: If the allocation of the output queue fails, nil is     *
  176. * returned.                                                                *
  177. *                                                                          *
  178. ***************************************************************************/
  179. - _init
  180. {
  181.   [super init];
  182.  
  183.   socketFd    = -1;   // A Clearly invalid value, for now.
  184.   manager     = nil;
  185.   user        = nil;
  186.   hostaddress = NULL;
  187.   hostname    = NULL;
  188.  
  189.   zone        = [self zone];
  190.  
  191.   outputQueue = (char *)NXZoneMalloc(zone, OUTQSIZE);
  192.   if (!outputQueue) {
  193.     SktReportSocketMemError(self, STR_MallocFatalError);
  194.     return [self free];
  195.   }
  196.   queueLength = 0;
  197.  
  198.   return self;
  199. }
  200.  
  201. /***************************************************************************
  202. *                                                                          *
  203. * -initOnFd:withManager:options:                                           *
  204. *                                                                          *
  205. * THIS IS ONE OF THREE DESIGNATED INITIALIZERS FOR THIS CLASS.  DO NOT     * 
  206. * EVER USE -init AS A DESIGNATED INITIALIZER.                              *
  207. *                                                                          *
  208. * This is the designated initializer for SktSocket objects in the server   *
  209. * program.  It initializes the socket to accept a connection on a file     *
  210. * descriptor, and records the SktSocketManager for further communication.  *
  211. *                                                                          *
  212. * ERROR CONDITIONS: The following conditions cause a return of nil:        *
  213. *                                                                          *
  214. * - No manager provided.                                                   *
  215. * - The socket can't be created/accepted.                                  *
  216. * - setSocketOptions: returns nil.                                         *
  217. * - The FNDELAY option can't be set.                                       *
  218. *                                                                          *
  219. ***************************************************************************/
  220. - initOnFd:(int)serviceSocketFd withManager:(SktSocketManager *)aManager 
  221. {
  222.   struct sockaddr_in  peername;
  223.   struct hostent     *peerinfo;
  224.   int                 i;
  225.  
  226.   [self _init];
  227.  
  228.  /*
  229.   * Do this right away!  (We can't log if there's no manager.)
  230.   */
  231.   if (aManager) manager = aManager;
  232.   else return [self free];
  233.  
  234.  /*
  235.   * Accept the connection to get the file desctiptor.
  236.   */
  237.   i = sizeof(peername);
  238.   socketFd = accept(serviceSocketFd, (struct sockaddr *) &peername, &i);
  239.  
  240.   if (0 > socketFd) {
  241.     [manager log:STR_CantAccept, [[self class] name]];
  242.     return [self free];
  243.   }
  244.  
  245.  /*
  246.   * Set options on the socket.  We have to do this
  247.   * AFTER the accept(), or accept() will return -1.
  248.   */
  249.   if (![self setSocketOptions:socketFd]) {
  250.     [manager log:STR_CantSetOpts, [self name], socketFd];
  251.     return [self free];
  252.   }
  253.  
  254.  /*
  255.   * Get the address of the connected host.  If the string duplication
  256.   * fails, report it, but don't return nil.  (This may need changing.)
  257.   */
  258.   hostaddress = zoneStrdup(zone, (char *)inet_ntoa(peername.sin_addr));
  259.   if (!hostaddress) {
  260.     SktReportSocketMemError(self, STR_MallocFatalError);
  261.   }
  262.  
  263.  /*
  264.   * Now muck around to get the name.  Copy the name of the connected
  265.   * host.  If the string duplication fails, report it, but don't
  266.   * return nil.  (This may need changing.)
  267.   */
  268. /// I think there's a bug in here (call to gethostbyaddr()) somewhere.
  269.   peerinfo = gethostbyaddr((char *)&peername.sin_addr,
  270.                            sizeof(peername.sin_addr), AF_INET);
  271.  
  272.   if (!peerinfo) hostname = NULL;
  273.   else hostname = zoneStrdup(zone, peerinfo->h_name);
  274.   if (!hostname) {
  275.     SktReportSocketMemError(self, STR_MallocFatalError);
  276.   }
  277.  
  278.  /*
  279.   * Log the connection.
  280.   */
  281.   if (manager && [manager doesLog]) {
  282.     [manager log:STR_NewConnect, [[self class] name],
  283.                  (hostname == NULL ? STR_UnknownParens : hostname),
  284.                  (hostaddress == NULL ? STR_Unknown : hostaddress),
  285.                  socketFd];
  286.   }
  287.   return self;
  288.  
  289. } /*initOnFd:withManager:options:*/
  290.  
  291. /***************************************************************************
  292. *                                                                          *
  293. * -initOnHostname:andPort:options:                                         *
  294. *                                                                          *
  295. * THIS IS ONE OF THREE DESIGNATED INITIALIZERS FOR THIS CLASS.  DO NOT     * 
  296. * EVER USE -init AS A DESIGNATED INITIALIZER.                              *
  297. *                                                                          *
  298. * This is the designated initializer for SktSocket objects in a client     *
  299. * program.  It initializes the socket to request a connection to a host by *
  300. * name and port.                                                           *
  301. *                                                                          *
  302. * ERROR CONDITIONS: The following conditions cause a return of nil:        *
  303. *                                                                          *
  304. * - No hostname provided, or host info can't be accessed.                  *
  305. * - The socket can't be created/connected.                                 *
  306. * - setSocketOptions: returns nil.                                         *
  307. * - The FNDELAY option can't be set.                                       *
  308. *                                                                          *
  309. ***************************************************************************/
  310. - initOnHostname:(char *)hostName andPort:(int)port
  311. {
  312.   struct sockaddr_in server;
  313.   struct hostent *host;
  314.  
  315.   if (!hostName) return [self free];
  316.  
  317.   [self _init];
  318.  
  319.  /*
  320.   * Get a new file descriptor.
  321.   */
  322.   socketFd = socket(AF_INET, SOCK_STREAM, 0);
  323.   if (0 > socketFd) {
  324.     if (stderr) fprintf(stderr, STR_CantOpenSocket, [[self class] name]);
  325.     return [self free];
  326.   }
  327.  
  328.  /*
  329.   * Try to get the host from the name.
  330.   */
  331.   server.sin_family = AF_INET;
  332.   host = gethostbyname(hostName);
  333.   if (!host) {
  334.     if (stderr) fprintf(stderr, STR_UnknownHost, [[self class] name],
  335.                         hostName);
  336.     return [self free];
  337.   }
  338.  
  339.  /*
  340.   * Now try to connect.
  341.   */
  342.   memcpy(&server.sin_addr, host->h_addr, host->h_length);
  343.   server.sin_port = htons(port);
  344.   if (0 > connect(socketFd, (struct sockaddr *)&server, sizeof(server))) {
  345.     if (stderr) fprintf(stderr, STR_CantConnectToServer,
  346.                        [[self class] name], socketFd);
  347.     return [self free];
  348.   }
  349.  
  350.  /*
  351.   * Set options on the socket.  We have to do this
  352.   * AFTER the connect(), or connect() will return -1.
  353.   */
  354.   if (![self setSocketOptions:socketFd]) {
  355.     if (stderr) fprintf(stderr, STR_CantSetOpts, [self name], socketFd);
  356.     return [self free];
  357.   }
  358.  
  359.  
  360.  /*
  361.   * Save the host name and address.  If a string duplication fails,
  362.   * report it, but don't return nil.  (This may need changing.)
  363.   */
  364.   hostname = zoneStrdup(zone, hostName);
  365.   if (!hostname) SktReportSocketMemError(self, STR_MallocFatalError);
  366.  
  367.   hostaddress = zoneStrdup(zone, (char *)inet_ntoa(host->h_addr));
  368.   if (!hostaddress) SktReportSocketMemError(self, STR_MallocFatalError);
  369.  
  370.   return self;
  371.  
  372. } /*initOnHostname:andPort:options:*/
  373.  
  374. /***************************************************************************
  375. *                                                                          *
  376. * -initOnAddress:andPort:options:                                          *
  377. *                                                                          *
  378. * THIS IS ONE OF THREE DESIGNATED INITIALIZERS FOR THIS CLASS.  DO NOT     * 
  379. * EVER USE -init AS A DESIGNATED INITIALIZER.                              *
  380. *                                                                          *
  381. * This is the designated initializer for SktSocket objects in a client     *
  382. * program.  It initializes the socket to request a connection to a host by *
  383. * internet address (NULL-terminated string in dot notation and port.       *
  384. *                                                                          *
  385. * ERROR CONDITIONS: The following conditions cause a return of nil:        *
  386. *                                                                          *
  387. * - No host address provided, or host info can't be accessed.              *
  388. * - The socket can't be created/connected.                                 *
  389. * - setSocketOptions: returns nil.                                         *
  390. * - The FNDELAY option can't be set.                                       *
  391. *                                                                          *
  392. ***************************************************************************/
  393. - initOnAddress:(char *)hostAddress andPort:(int)port
  394. {
  395.   struct sockaddr_in  server;
  396.   long int            hostaddr;
  397.   struct hostent     *host;
  398.  
  399.   [self _init];
  400.  
  401.  /*
  402.   * Get a new file descriptor.
  403.   */
  404.   socketFd = socket(AF_INET, SOCK_STREAM, 0);
  405.   if (0 > socketFd) {
  406.     if (stderr) fprintf(stderr, STR_CantOpenSocket, [[self class] name]);
  407.     [self free];
  408.   }
  409.  
  410.  /*
  411.   * Get host info based on the address.
  412.   */
  413.   hostaddr = inet_addr(hostAddress);
  414.   host = gethostbyaddr((char *)&hostaddr, sizeof(hostaddr), AF_INET);
  415.   if (!host && stderr) {
  416.     if (stderr) fprintf(stderr, STR_UnknownHost, [[self class] name],
  417.                         hostAddress);
  418.     return [self free];
  419.   }
  420.  
  421.  /*
  422.   * Now try to connect.
  423.   */
  424.   server.sin_family = AF_INET;
  425.   memcpy(&server.sin_addr, host->h_addr, host->h_length);
  426.   server.sin_port = htons(port);
  427.   if (0 > connect(socketFd, (struct sockaddr *)&server, sizeof(server))) {
  428.     if (stderr) fprintf(stderr, STR_CantConnectToServer,
  429.                         [[self class] name]);
  430.     return [self free];
  431.   }
  432.  
  433.  /*
  434.   * Set options on the socket.  We have to do this
  435.   * AFTER the connect(), or connect() will return -1.
  436.   */
  437.   if (![self setSocketOptions:socketFd]) {
  438.     if (stderr) fprintf(stderr, STR_CantSetOpts, [self name], socketFd);
  439.     return [self free];
  440.   }
  441.  
  442.  /*
  443.   * Save the host name and address.  If a string duplication fails,
  444.   * report it, but don't return nil.  (This may need changing.)
  445.   */
  446.   hostname = zoneStrdup(zone, host->h_name);
  447.   if (!hostname) SktReportSocketMemError(self, STR_MallocFatalError);
  448.  
  449.   hostaddress = zoneStrdup(zone, hostAddress);
  450.   if (!hostaddress) SktReportSocketMemError(self, STR_MallocFatalError);
  451.  
  452.   return self;
  453.  
  454. } /*initOnAddress:andPort:options:*/
  455.  
  456. /***************************************************************************
  457. *                                                                          *
  458. * -setSocketOptions:                                                       *
  459. *                                                                          *
  460. * This method tries to set the FNDELAY option on the socket so it can      *
  461. * handle asynchronous writes.  It may be overriden in subclasses to set    *
  462. * other socket/file descriptor options via setsockopt/fcntl.               *
  463. *                                                                          *
  464. * If this method returns nil, then any initialization is aborted and the   *
  465. * init... method will return nil.                                          *
  466. *                                                                          *
  467. ***************************************************************************/
  468. - setSocketOptions:(int)fd
  469. {
  470.   if (-1 == fcntl(fd, F_SETFL, FNDELAY)) {
  471.     if (manager) [manager log:STR_CantSetFNDELAY, [[self class] name], fd];
  472.     if (stderr) fprintf(stderr, STR_CantSetFNDELAY, [[self class] name], fd);
  473.     return nil;
  474.   }
  475.  
  476.   return self;
  477. }
  478.  
  479. /***************************************************************************
  480. *                                                                          *
  481. * -close                                                                   *
  482. *                                                                          *
  483. * Properly closes the socket by notifying the SktSocketManager if there is *
  484. * one (which will send it a -free message), otherwise just freeing.        *
  485. *                                                                          *
  486. ***************************************************************************/
  487. - close
  488. {
  489.   if (manager) [manager closeSocket:self];
  490.   else return [self free];
  491.  
  492.   return nil;  // Just in case;
  493. }
  494.  
  495. /***************************************************************************
  496. *                                                                          *
  497. * -free                                                                    *
  498. *                                                                          *
  499. * Really closes the socket.  Does all the ususal stuff too.  Does not free *
  500. * the user; it is responsible for noticing its socket is gone, and taking  *
  501. * appropriate action.                                                      *
  502. *                                                                          *
  503. ***************************************************************************/
  504. - free
  505. {
  506.   if (hostaddress) NXZoneFree(zone, hostaddress);
  507.   if (hostname)    NXZoneFree(zone, hostname);
  508.   if (outputQueue) NXZoneFree(zone, outputQueue);
  509.  
  510.   close(socketFd);
  511.   if (user && self == [user socket]) [user setSocket:nil];
  512.   return [super free];
  513. }
  514.  
  515. /***************************************************************************
  516. *                                                                          *
  517. * -setUser:                                                                *
  518. *                                                                          *
  519. * Sets the user object used to that supplied.  This method also mucks with *
  520. * the old and new users, to properly remove self as the old user's socket  *
  521. * without causing an infinite loop.  This is doable with methods, but the  *
  522. * classes are so tightly bound, it's not really worth the code bulk or     *
  523. * cost of a message send.                                                  *
  524. *                                                                          *
  525. ***************************************************************************/
  526. - setUser:(SktSocketUser *)aUser
  527. {
  528.   SktSocketUser *oldUser;
  529.  
  530.   oldUser = user;
  531.   user = aUser;
  532.  
  533.  /*
  534.   * We need to muck with pointers because messaging would cause an
  535.   * infinite loop.
  536.   */
  537.   if (oldUser) ((sktsocketuser *)oldUser)->socket = nil;
  538.   if (user) ((sktsocketuser *)user)->socket = self;
  539.   return oldUser;
  540. }
  541.  
  542. /***************************************************************************
  543. *                                                                          *
  544. * -user                                                                    *
  545. *                                                                          *
  546. * Returns the user (an instance of SktSocketUser or a subclass) associated *
  547. * with this SktSocket.                                                     *
  548. *                                                                          *
  549. ***************************************************************************/
  550. - (SktSocketUser *)user
  551. {
  552.   return user;
  553. }
  554.  
  555. /***************************************************************************
  556. *                                                                          *
  557. * -socketFd                                                                *
  558. *                                                                          *
  559. * Returns the file descriptor for the socket.                              *
  560. *                                                                          *
  561. ***************************************************************************/
  562. - (int) socketFd
  563. {
  564.   return socketFd;
  565. }
  566.  
  567. /***************************************************************************
  568. *                                                                          *
  569. * -manager                                                                 *
  570. *                                                                          *
  571. * If the SktSocket has a manager (which it does if it's on the server),    *
  572. * this returns it.                                                         *
  573. *                                                                          *
  574. ***************************************************************************/
  575. - (struct SktSocketManager *)manager
  576. {
  577.   return manager;
  578. }
  579.  
  580. /***************************************************************************
  581. *                                                                          *
  582. * -hostaddress                                                             *
  583. *                                                                          *
  584. * Returns the address of the host the SktSocket's process ison, in dot     *
  585. * notation.                                                                *
  586. *                                                                          *
  587. ***************************************************************************/
  588. - (const char *)hostaddress
  589. {
  590.   return hostaddress;
  591. }
  592.  
  593. /***************************************************************************
  594. *                                                                          *
  595. * -hostname                                                                *
  596. *                                                                          *
  597. * Returns the name of the host the SktSocket's process is on.              *
  598. *                                                                          *
  599. ***************************************************************************/
  600. - (const char *)hostname
  601. {
  602.   return hostname;
  603. }
  604.  
  605. /***************************************************************************
  606. *                                                                          *
  607. * -readInput                                                               *
  608. *                                                                          *
  609. * Performs a read(2) on the socketFd, and has the user, if any, queue the  *
  610. * data read as input.  If there is no user, or an error other than         *
  611. * EWOULDBLOCK occurs, or if the socketFd is at EOF, return nil.            *
  612. *                                                                          *
  613. * All the log messages from this method are sent only to the manager; that *
  614. * only, only when the SktSocket is in a server process.                    *
  615. *                                                                          *
  616. ***************************************************************************/
  617. - readInput
  618. {
  619.   char   buf[BUFSIZ];
  620.   int    bytesread = 0;
  621.  
  622.   if (![self user]) return nil;
  623.  
  624.   if (0 < (bytesread = read(socketFd, buf, BUFSIZ))) {
  625.     [[self user] queueInput:(const char *)buf ofLength:bytesread];
  626.     return self;
  627.   }
  628.   else if (0 > bytesread) {
  629.     if (EWOULDBLOCK == errno) return self;
  630.     else {      
  631.       if (manager) [manager log:STR_ReadError,
  632.                     [[self class] name], socketFd];
  633.       else if (stderr) fprintf(stderr, STR_ReadError,
  634.                     [[self class] name], socketFd);
  635.       return nil;
  636.     }
  637.   }
  638.   else  {
  639.     if (manager && [manager doesLog]) {
  640.       [manager log:STR_NoMoreInput, [[self class] name], socketFd];
  641.     }
  642.     return nil;
  643.   }
  644. }
  645.  
  646. /***************************************************************************
  647. *                                                                          *
  648. * -queueOutput:ofLength:                                                   *
  649. *                                                                          *
  650. * Adds output to the output queue and adjusts the length.                  *
  651. *                                                                          *
  652. * ERROR CONDITION: If the reallocation fails, a message is logged, and nil *
  653. * is returned.  The output queue won't exist if this method returns nil,   *
  654. * so you should consider a nil return as a fatal condition and either free *
  655. * the object, or exit the program.                                         *
  656. *                                                                          *
  657. ***************************************************************************/
  658. - queueOutput:(const char *)output ofLength:(long int)length
  659. {
  660.   unsigned int fullLength;
  661.  
  662.  /*
  663.   * I hope nobody is this stupid.
  664.   */
  665.   if (0 >= length) return self;
  666.  
  667.  /*
  668.   * Find out how much text we're dealing with, check if we need more space,
  669.   * and smash 'em together.  Also update the new queue length.
  670.   */
  671.   fullLength = length + queueLength;
  672.  
  673.   if (malloc_size(outputQueue) <= fullLength) {
  674.     outputQueue = (char *)NXZoneRealloc(zone, outputQueue, fullLength);
  675.     if (!outputQueue) {
  676.       SktReportSocketMemError(self, STR_ReallocFatalError);
  677.       queueLength = 0;
  678.       return nil;
  679.     }
  680.   }
  681.   memcpy(outputQueue+queueLength, output, length);
  682.  
  683.   queueLength = fullLength;
  684.  
  685.   return self;
  686. }
  687.  
  688. /***************************************************************************
  689. *                                                                          *
  690. * -queueOutputString:                                                      *
  691. *                                                                          *
  692. * A convenience for queueing NULL-terminated strings.                      *
  693. *                                                                          *
  694. ***************************************************************************/
  695. - queueOutputString:(const char *)aString
  696. {
  697.   return (*outputToSocket)(self, @selector(queueOutput:ofLength:),
  698.                            aString, strlen(aString));
  699. }
  700.  
  701. /***************************************************************************
  702. *                                                                          *
  703. * -flushOutput                                                             *
  704. *                                                                          *
  705. * Flushes the output queue to the socketFd.  Doesn't try to write after an *
  706. * EWOULDBLOCK error occurs.                                                *
  707. *                                                                          *
  708. * All the log messages from this method are sent only to the manager; that *
  709. * only, only when the SktSocket is in a server process.                    *
  710. *                                                                          *
  711. * ERROR CONDITION: If the reallocation fails, a message is logged, and nil *
  712. * is returned.  The output queue won't exist if this method returns nil,   *
  713. * so you should consider a nil return as a fatal condition and either free *
  714. * the object, or exit the program.                                         *
  715. *                                                                          *
  716. ***************************************************************************/
  717. - flushOutput
  718. {
  719.   long int   writeLength;  // how much to write
  720.   long int   written;      // how much written so far
  721.   int        writeResult;  // how much written this write()
  722.   extern int errno;        // in case Something Bad happens
  723.  
  724.   writeLength = queueLength;
  725.   written = 0;
  726.  
  727.  /*
  728.   * Big loop until all output is writte, an EWOULDBLOCK occurs, or
  729.   * an other error occurs.
  730.   */
  731.   while (written < writeLength) {
  732.  
  733.    /*
  734.     * Be careful with the args to write here.  It expects int's,
  735.     * not long int's or differences thereof.
  736.     */
  737.     writeResult = write(socketFd, (outputQueue + written),
  738.                         INT_MAX <= (writeLength - written) ?
  739.                         INT_MAX : (writeLength - written));
  740.  
  741.    /*
  742.     * If we get an EWOULDBLOCK, don't hang around trying to write; we'll
  743.     * have another chance later.  If the error is something else, log it.
  744.     */
  745.     if (0 > writeResult) {
  746.  
  747.       if (EWOULDBLOCK != errno) {
  748.         if (manager)
  749.           [manager log:STR_WriteError, [[self class] name], socketFd];
  750.         else if (stderr)
  751.           fprintf(stderr, STR_WriteError, [[self class] name], socketFd);
  752.  
  753.         break;
  754.       }
  755.     }
  756.  
  757.     written += writeResult;
  758.  
  759.   } /*while(written < writeLength)*/
  760.  
  761.  
  762.  /*
  763.   * Shrink the queue if it's empty and really big, or just pull it back.
  764.   */
  765.   if (written >= writeLength) {
  766.     if (malloc_size(outputQueue) > OUTQSIZE) {
  767.       outputQueue = (char *)NXZoneRealloc(zone, outputQueue, OUTQSIZE);
  768.       if (!outputQueue) {
  769.         SktReportSocketMemError(self, STR_ReallocFatalError);
  770.         queueLength = 0;
  771.         return nil;
  772.       }
  773.     }
  774.   }
  775.   else {
  776.     memmove(outputQueue, outputQueue+written, writeLength-written+1);
  777.   }
  778.  
  779.  /*
  780.   * Update the queue length.
  781.   */
  782.   queueLength = writeLength - written;
  783.  
  784.   return self;
  785.  
  786. } /*flushOutput*/
  787.  
  788. /***************************************************************************
  789. *                                                                          *
  790. * -purgeOutput                                                             *
  791. *                                                                          *
  792. * Marks the queue as empty, and shrinks it if needed.                      *
  793. *                                                                          *
  794. ***************************************************************************/
  795. - purgeOutput
  796. {
  797.   queueLength = 0;
  798.  
  799.   if (malloc_size(outputQueue) > OUTQSIZE) {
  800.  
  801.     outputQueue = (char *)NXZoneRealloc(zone, outputQueue, OUTQSIZE);
  802.  
  803.     if (!outputQueue) {
  804.       SktReportSocketMemError(self, STR_ReallocFatalError);
  805.       queueLength = 0;
  806.       return nil;
  807.     }
  808.   }
  809.  
  810.   return self;
  811. }
  812.  
  813. @end /*implementation SktSocket*/
  814.  
  815. /***************************************************************************
  816. ***************************************************************************/
  817.